package cbbx_sm.parser;

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Collections;
import java.util.List;
import java.util.Vector;

import cbbx_sm.utils.CameraDataFixer;
import cbbx_sm.utils.ExperimentManager;
import cbbx_sm.utils.Timestamp;

/**
 * This class parse data according to the following format:
 * 
 * FILENAME ( ENTITY_NUMBER 
 * 			  BBOX0   X_UL   Y_UL   X_BR   Y_BR   BLUE   GREEN   RED
 * 			  BBOX1   X_UL   Y_UL   X_BR   Y_BR   BLUE   GREEN   RED
 * 			  BBOX2   X_UL   Y_UL   X_BR   Y_BR   BLUE   GREEN   RED
 * 			  BBOX3   X_UL   Y_UL   X_BR   Y_BR   BLUE   GREEN   RED )
 * 
 * @author Alessio
 */
public class Parser {
	
	/**
	 * Parse a file of frames.
	 * 
	 * @param filename
	 * @return
	 * @throws IOException
	 */
	public static CameraData parseFile(String filename, String cameraId) throws IOException {
		BufferedReader in =
			new BufferedReader(
			    new InputStreamReader(
			        ExperimentManager.isJarred ? 
			        		ClassLoader.getSystemResourceAsStream("/" + filename) : new FileInputStream(filename)));	
		CameraData data = new CameraData(cameraId);
		List<Frame> frames = new Vector<Frame>();
		String line;
		
		// Line per line scan
		while ((line = in.readLine()) != null){
			// Current analyzed frame...
			Frame curFrame = null;
			if (ExperimentManager.useManualDataFormat) {
				curFrame = parseManualFrame(line, cameraId);
			} else {
				curFrame = parseFrame(line, cameraId);
			}
			frames.add(curFrame);
		}
		
		// Important to sort by time as we assume that the data is sorted that way.
		Collections.sort(frames);
		
		data.setFrames(frames);
		if (ExperimentManager.fixCameraData) {
			CameraDataFixer.fixCameraData(data);
		}
		
		if (ExperimentManager.isViewEnabled) {
			data.setImageFile("data/images/"+cameraId+".jpg");
		}
		return data;
	}
	public static Frame parseManualFrame(String frameInfo, String cameraId) {
		// Current analyzed frame...
		Frame frame = null;
		
		// Dividing each line in tokens 			
		String[] tokens = frameInfo.split("\t");
		String imageFilename = tokens[0];
		long frameTimestamp = getTimeStamp(tokens[0]);
		
		// Frame doesn't contain any bounding box.
		if (tokens.length == 1){
			frame = new Frame(imageFilename, new Timestamp(frameTimestamp), null, cameraId);
		}
		// Frame contains one or more entities, so create all entities and
		// at the end create the frame object.
		else {
			/*
			 * The concept is:
			 * 		tokens.length - 1 ==> delete filename
			 * 		1 ==> tokens per entity
			 */
			int numberOfEntities = (tokens.length - 1);
			List<Entity> entities = new Vector<Entity>(numberOfEntities);
			
			/*
			 * For each entity create an Entity object and add it to the
			 * 'entities' list.
			 */
			
			// For parsing only one entity per frame
//			int value = numberOfEntities > 0 ? 1 : 0;
			for (int i = 0; i < numberOfEntities /*value*/; i++){
				Entity curEntity = null;
				
				String entityId = String.valueOf(i);
				BoundingBox[] boxes  = new BoundingBox[4];
				
				/*
				 * For each entity there are 4 bounding boxes generated identiacal to the facial box
				 * so here BoundingBox object are created and assigned to the
				 * current entity.
				 */
				for (int j = 0; j < 4; j++){
					int x = Integer.parseInt(tokens[1 + i].split(" ")[0]);
					int y = Integer.parseInt(tokens[1 + i].split(" ")[1]);
					int x_ul = x - 20;
					int y_ul = y - 20;
					int x_br = x + 20;
					int y_br = y + 20;
					int blue = 0;
					int green = 0;
					int red = 0;
					
					boxes[j] = new BoundingBox(x_ul, y_ul, x_br, y_br, blue, green, red);
				}
				
				/*
				 * New entity creation and add to the entities list.
				 */
				curEntity = new Entity(entityId, boxes);
				entities.add(curEntity);
			}
			
			/*
			 * New frame instantiation.
			 */
			frame = new Frame(imageFilename, new Timestamp(frameTimestamp), entities, cameraId);
		}
		
		return frame;
	}
	
	/**
	 * Parse a frame from a string in the specified format.
	 * 
	 * @param frameInfo
	 * @return
	 */
	public static Frame parseFrame(String frameInfo, String cameraId){
		// Current analyzed frame...
		Frame frame = null;
		
		// Dividing each line in tokens 			
		String[] tokens = frameInfo.split("\t");
		String imageFilename = tokens[0];
		long frameTimestamp = getTimeStamp(tokens[0]);
		
		// Frame doesn't contain any bounding box.
		if (tokens.length == 1){
			frame = new Frame(imageFilename, new Timestamp(frameTimestamp), null, cameraId);
		}
		// Frame contains one or more entities, so create all entities and
		// at the end create the frame object.
		else {
			/*
			 * The concept is:
			 * 		tokens.length - 1 ==> delete filename
			 * 		33 ==> tokens per entity
			 */
			int numberOfEntities = (tokens.length - 1) / 33;
			List<Entity> entities = new Vector<Entity>(numberOfEntities);
			
			/*
			 * For each entity create an Entity object and add it to the
			 * 'entities' list.
			 */
			
			// For parsing only one entity per frame
//			int value = numberOfEntities > 0 ? 1 : 0;
			for (int i = 0; i < numberOfEntities /*value*/; i++){
				Entity curEntity = null;
				
				String entityId = tokens[1 + 33 * i];
				BoundingBox[] boxes  = new BoundingBox[4];
				
				/*
				 * For each entity there are 4 bounding box; so here 
				 * BoundingBox object are created and assigned to the
				 * current entity.
				 */
				for (int j = 0; j < 4; j++){
					// Not necessary now...
//					int boundingBoxId = Integer.parseInt(tokens[2 + j * 8 + 33 * i]);
					int x_ul = Integer.parseInt(tokens[3 + j * 8 + 33 * i]);
					int y_ul = Integer.parseInt(tokens[4 + j * 8 + 33 * i]);
					int x_br = Integer.parseInt(tokens[5 + j * 8 + 33 * i]);
					int y_br = Integer.parseInt(tokens[6 + j * 8 + 33 * i]);
					int blue = Integer.parseInt(tokens[7 + j * 8 + 33 * i]);
					int green = Integer.parseInt(tokens[8 + j * 8 + 33 * i]);
					int red = Integer.parseInt(tokens[9 + j * 8 + 33 * i]);
					
					boxes[j] = new BoundingBox(x_ul, y_ul, x_br, y_br, blue, green, red);
				}
				
				/*
				 * New entity creation and add to the entities list.
				 */
				curEntity = new Entity(entityId, boxes);
				entities.add(curEntity);
			}
			
			/*
			 * New frame instantiation.
			 */
			frame = new Frame(imageFilename, new Timestamp(frameTimestamp), entities, cameraId);
		}
		
		return frame;
	}
	
	/**
	 * The string 'framename' must have the form: "(ID)_(TIMESTAMP).(EXTENSION)"
	 
	 * @param framename
	 * @return
	 */
	private static long getTimeStamp(String framename){
		try{ 
			String[] tokens = framename.split("_")[1].split("\\.");
			return Long.parseLong(tokens[0]); 
		}
		catch(Throwable t){
			return -1;
		}
	}
}
